//
// Created by gogoqiu on 2021/10/19.
//

#include "wifi_monitor.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <asm/types.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/if.h>
#include <linux/rtnetlink.h>
#include <stdlib.h>
#include <time.h>
#include <wx/textfile.h>
#include <execinfo.h>
#include "log.h"

/**
 * ENTRY ( RTM_NEWADDR )
 * { RTM_NEWADDR, #RTM_NEWADDR }
 * 如何实现系统启动自动后台执行？
 * **/
#define ENTRY(x) {x, #x}
#define CMD "/home/gogoqiu/mysystem/bin/SetGitIP"


//如何在
//windows版本
/***
 * 事件序列比较混乱
 * 下线
 * netlink message: len = 64, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x11043, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_RUNNING IFF_MULTICAST IFF_LOWER_UP 			//IFF_RUNNING
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x11043, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_RUNNING IFF_MULTICAST IFF_LOWER_UP 			//IFF_RUNNING
netlink message: len = 72, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1043, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_RUNNING IFF_MULTICAST
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1003, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_MULTICAST
netlink message: len = 88, type = 21, flags = 0x0, seq = 3528, pid = 3649045442
        ifi_family = 2, ifi_type = 0, ifi_index = 3, ifi_flags = 0x10008, ifi_change = 0xAA05A8C0
                Msg Type: RTM_DELADDR																														//RTM_DELADDR
                flags: IFF_LOOPBACK IFF_LOWER_UP(C)
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1003, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_MULTICAST
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1002, ifi_change = 0x1
                Msg Type: RTM_NEWLINK
                flags: IFF_BROADCAST IFF_MULTICAST
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1002, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_BROADCAST IFF_MULTICAST
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1003, ifi_change = 0x1
                Msg Type: RTM_NEWLINK
                flags: IFF_UP(C) IFF_BROADCAST IFF_MULTICAST
netlink message: len = 64, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1003, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_MULTICAST
netlink message: len = 64, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1003, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_MULTICAST

*/
/**
 * 上线
 * 唯一消息？
 *
 * netlink message: len = 64, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1003, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_MULTICAST
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1002, ifi_change = 0x1
                Msg Type: RTM_NEWLINK
                flags: IFF_BROADCAST IFF_MULTICAST
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1002, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_BROADCAST IFF_MULTICAST
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1003, ifi_change = 0x1
                Msg Type: RTM_NEWLINK
                flags: IFF_UP(C) IFF_BROADCAST IFF_MULTICAST
netlink message: len = 64, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x1003, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_MULTICAST
netlink message: len = 184, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x11003, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_MULTICAST IFF_LOWER_UP
netlink message: len = 72, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x11003, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_MULTICAST IFF_LOWER_UP
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x11003, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_MULTICAST IFF_LOWER_UP
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x11043, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_RUNNING IFF_MULTICAST IFF_LOWER_UP 	//IFF_RUNNING,
netlink message: len = 1296, type = 16, flags = 0x0, seq = 0, pid = 0
        ifi_family = 0, ifi_type = 1, ifi_index = 3, ifi_flags = 0x11043, ifi_change = 0x0
                Msg Type: RTM_NEWLINK
                flags: IFF_UP IFF_BROADCAST IFF_RUNNING IFF_MULTICAST IFF_LOWER_UP 	//IFF_RUNNING,
netlink message: len = 88, type = 20, flags = 0x0, seq = 3562, pid = 3649045442															//seq
        ifi_family = 2, ifi_type = 0, ifi_index = 3, ifi_flags = 0x10008, ifi_change = 0xAA05A8C0
                Msg Type: RTM_NEWADDR
                flags: IFF_LOOPBACK IFF_LOWER_UP(C)
netlink message: len = 88, type = 20, flags = 0x0, seq = 3570, pid = 3649045442															//seq
        ifi_family = 2, ifi_type = 0, ifi_index = 3, ifi_flags = 0x10008, ifi_change = 0xAA05A8C0
                Msg Type: RTM_NEWADDR
                flags: IFF_LOOPBACK IFF_LOWER_UP(C) 																									//RTM_NEWADDR
 * */
/**
 * https://www.xuebuyuan.com/3209683.html
 *
设备标志
IFF_UP	接口正在运行.														//出现了
IFF_BROADCAST	有效的广播地址集.							//出现了
IFF_DEBUG	内部调试标志.
IFF_LOOPBACK	这是自环接口.									//出现了
IFF_POINTOPOINT	这是点到点的链路接口.
IFF_RUNNING	资源已分配.												//出现了
IFF_NOARP	无arp协议, 没有设置第二层目的地址.
IFF_PROMISC	接口为杂凑(promiscuous)模式.
IFF_NOTRAILERS	避免使用trailer .
IFF_ALLMULTI	接收所有组播(multicast)报文.
IFF_MASTER	主负载平衡群(bundle).
IFF_SLAVE	从负载平衡群(bundle).
IFF_MULTICAST	支持组播(multicast).						//出现了
IFF_PORTSEL	可以通过ifmap选择介质(media)类型.
IFF_AUTOMEDIA	自动选择介质.
IFF_DYNAMIC	接口关闭时丢弃地址.
 * **/
/***
 * IFF_RUNNING表示该接口已被激活，且可以正常传输数据
 * IFF_UP表示giant接口已被激活，但可能无法正常传输数据，如网线未连接的情况
 * IFF_LOWER_UP表示网络的物理连接已就绪，即网线连接正常；
 * **/
/**
 * ifi_type	代表硬件设备的类型：
ARPHRD_ETHER                   M以太网
ARPHRD_PPP                     PPP拨号
ARPHRDLOOPBACK                 环路设备
 *
 * **/
/**
 *
 * **/
struct {
    unsigned flag;
    const char *name;
} ifi_flag_map[] = {
        ENTRY ( IFF_UP ),
        ENTRY ( IFF_BROADCAST ),
        ENTRY ( IFF_DEBUG ),
        ENTRY ( IFF_LOOPBACK ),
        ENTRY ( IFF_POINTOPOINT ),
        ENTRY ( IFF_NOTRAILERS ),
        ENTRY ( IFF_RUNNING ),
        ENTRY ( IFF_NOARP ),
        ENTRY ( IFF_PROMISC ),
        ENTRY ( IFF_ALLMULTI ),
        ENTRY ( IFF_MASTER ),
        ENTRY ( IFF_SLAVE ),
        ENTRY ( IFF_MULTICAST ),
        ENTRY ( IFF_PORTSEL ),
        ENTRY ( IFF_AUTOMEDIA ),
        ENTRY ( IFF_DYNAMIC ),
        ENTRY ( IFF_LOWER_UP ),
        ENTRY ( IFF_DORMANT ),
        ENTRY ( IFF_ECHO ),
};

struct {
    unsigned type;
    const char *name;
} nlmrt_type_map[] = {
        ENTRY ( RTM_NEWLINK ),						//{ RTM_NEWLINK, "RTM_NEWLINK" }
        ENTRY ( RTM_DELLINK ),
        ENTRY ( RTM_GETLINK ),
        ENTRY ( RTM_SETLINK ),
        ENTRY ( RTM_NEWADDR ),											//RTM_NEWADDR
        ENTRY ( RTM_DELADDR ),
        ENTRY ( RTM_GETADDR ),
        ENTRY ( RTM_NEWROUTE    ),
        ENTRY ( RTM_DELROUTE ),
        ENTRY ( RTM_GETROUTE ),
        ENTRY ( RTM_NEWNEIGH    ),
        ENTRY ( RTM_DELNEIGH ),
        ENTRY ( RTM_GETNEIGH ),
        ENTRY ( RTM_NEWRULE ),
        ENTRY ( RTM_DELRULE ),
        ENTRY ( RTM_GETRULE ),
        ENTRY ( RTM_NEWQDISC    ),
        ENTRY ( RTM_DELQDISC ),
        ENTRY ( RTM_GETQDISC ),
        ENTRY ( RTM_NEWTCLASS   ),
        ENTRY ( RTM_DELTCLASS ),
        ENTRY ( RTM_GETTCLASS ),
        ENTRY ( RTM_NEWTFILTER  ),
        ENTRY ( RTM_DELTFILTER ),
        ENTRY ( RTM_NEWACTION   ),
        ENTRY ( RTM_DELACTION ),
        ENTRY ( RTM_GETACTION ),
        ENTRY ( RTM_NEWPREFIX   ),
        ENTRY ( RTM_GETMULTICAST ),
        ENTRY ( RTM_GETANYCAST  ),
        ENTRY ( RTM_NEWNEIGHTBL ),
        ENTRY ( RTM_GETNEIGHTBL ),
        ENTRY ( RTM_SETNEIGHTBL ),
        ENTRY ( RTM_NEWNDUSEROPT ),
        ENTRY ( RTM_NEWADDRLABEL ),
        ENTRY ( RTM_DELADDRLABEL ),
        ENTRY ( RTM_GETADDRLABEL ),
        ENTRY ( RTM_GETDCB ),
        ENTRY ( RTM_SETDCB ),
        ENTRY ( RTM_NEWNETCONF ),
        ENTRY ( RTM_GETNETCONF ),
        ENTRY ( RTM_NEWMDB ),
        ENTRY ( RTM_DELMDB ),
        ENTRY ( RTM_GETMDB ),
};

#include "curl/curl.h"



int WifiMonitorAction::respLength = 0;
char* WifiMonitorAction::responseText = nullptr;

#define URL "http://aliyun/usr/bin/ip.php?type=SetGitIP"
/**
  收集消息
  fwrite(ptr, size, nmemb, stream);
    ptr-- 这是指向要被写入的元素数组的指针。
    size-- 这是要被写入的每个元素的大小，以字节为单位。
    nmemb-- 这是元素的个数，每个元素的大小为 size 字节。
    stream-- 这是指向 FILE 对象的指针，该 FILE 对象指定了一个输出流。
  size_t fwrite(const void *ptr, size_t size, size_t num_mem_byte, FILE *stream)
 */
size_t WifiMonitorAction::accumulate(void* buffer,size_t size,size_t num_mem_byte,void *stream/** ignore **/)
{
    auto *p = (WifiMonitorAction*)stream;
    int oldLength = WifiMonitorAction::respLength;
    WifiMonitorAction::respLength += size * num_mem_byte;
    WifiMonitorAction::responseText = (char*)realloc( WifiMonitorAction::responseText, WifiMonitorAction::respLength );
    memcpy( &WifiMonitorAction::responseText[oldLength], buffer, size * num_mem_byte );
    //p->tracer->Trace( responseText );
    add_log( responseText );
    return size*num_mem_byte;
}

void WifiMonitorAction::refresh(){
    if(responseText)
        ::free(responseText);
    respLength = 0;
}

//
int WifiMonitorAction::SetGitIP( const char* url )
{
    refresh();
    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();
    if (!curl)
    {
        //qDebug()<<("curl init failed!");
        //tracer->TraceError(_("curl init failed!"));
        add_log( wxT("curl init failed!" ));
        return -1;
    }

    curl_easy_setopt(curl,CURLOPT_URL, url ); //url地址
    curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION, accumulate ); //对返回的数据进行操作
    //这是write_data的第四个参数值,
    curl_easy_setopt(curl,CURLOPT_WRITEDATA, this );

    curl_easy_setopt(curl,CURLOPT_POST,1); //设置问非0表示本次操作为post
    //curl_easy_setopt(curl,CURLOPT_VERBOSE,1); //打印调试信息
    //curl_easy_setopt(curl,CURLOPT_HEADER,1); //将响应头信息和相应体一起传给
    curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1); //设置为非0,响应头信息location
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);//接收数据时超时设置，如果10秒内数据未
    //curl_easy_setopt(curl,CURLOPT_COOKIEFILE,"/Users/zhu/CProjects

    res = curl_easy_perform(curl);

    if (res != CURLE_OK)
    {
        switch(res)
        {
            case CURLE_UNSUPPORTED_PROTOCOL:
                //fprintf(stderr,"不支持的协议,由URL的头部指定\n");
                //tracer->TraceError(_("不支持的协议,由URL的头部指定"));
                add_log( wxT("不支持的协议,由URL的头部指定") );
            case CURLE_COULDNT_CONNECT:
                //fprintf(stderr,"不能连接到remote主机或者代理\n");
                //tracer->TraceError(_("不能连接到remote主机或者代理"));
                add_log( wxT("不能连接到remote主机或者代理") );
            case CURLE_HTTP_RETURNED_ERROR:
                //fprintf(stderr,"http返回错误\n");
                //tracer->TraceError(_("HTTP返回错误"));
                add_log( wxT("HTTP返回错误") );
            case CURLE_READ_ERROR:
                //fprintf(stderr,"读本地文件错误\n");
                //tracer->TraceError(_("读本地文件错误"));
                add_log( wxT("读本地文件错误") );
            default: {
                //fprintf(stderr,"返回值:%d\n",res);
                wxString temp;
                temp.sprintf( _("返回值:%d"),res );
                //tracer->TraceError( temp );
                add_log( temp );
            }
        }
        //emit addLog("curl fail!");
        return -1;
    }
    //emit addLog(responseText);
    return 0;
}

//
void WifiMonitorAction::OnTrigger()
{
    count ++;
    if ( count % 2 == 0 ) {
        //tracer->TraceError( "Trigger to curl!" );
        add_log( wxT("Trigger to curl!") );
        SetGitIP( URL );
    }
}

//
void WifiMonitorAction::print_type ( unsigned type )
{
    size_t i;

    //
    for ( i = 0; i < sizeof nlmrt_type_map / sizeof nlmrt_type_map[0]; i++ ) {
        if ( type == RTM_NEWADDR  ) {
            //tracer->TraceError( "RTM_NEWADDR: trigger to set new git ip!" );
            add_log( wxT("RTM_NEWADDR: trigger to set new git ip!") );
            OnTrigger();
            return;
        }else if ( type == nlmrt_type_map[i].type ) {
            //printf ( "\t\tMsg Type: %s\n", nlmrt_type_map[i].name );
            wxString temp;
            temp.sprintf( wxT("Msg Type: %s" ), nlmrt_type_map[i].name  );
            //tracer->TraceError( temp );
            add_log( temp );
            return;
        }
    }
    ///emit addLog( ( "\t\tMsg Type: unknown(%d)\n", type );
    wxString temp;
    temp.sprintf( "\t\tMsg Type: unknown(%d)", type  );
    //tracer->TraceError( temp );
    add_log( temp );
}

//
void WifiMonitorAction::print_flags ( unsigned flags, unsigned change )
{
    size_t i;

    //printf ( "\t\tflags: " );
    //tracer->TraceError( "flags:" );

    //
    for ( i = 0; i < sizeof ifi_flag_map / sizeof ifi_flag_map[0]; i++ ) {
        if ( flags & ifi_flag_map[i].flag ) {
            if ( change & ifi_flag_map[i].flag ) {
                //printf ( "%s(C) ", ifi_flag_map[i].name );
                wxString temp;
                temp.sprintf( "%s(C) ", ifi_flag_map[i].name );
                //tracer->TraceError( temp );
                add_log( temp );
            } else {
                //printf ( "%s ", ifi_flag_map[i].name );
                wxString temp;
                temp.sprintf( "%s", ifi_flag_map[i].name );
                //tracer->TraceError( temp );
                add_log( temp );
            }
        }
    }
    //puts ( "" );
}

//
void WifiMonitorAction::read_msg ( int fd )
{
    //qDebug()<<"read_msg";
    int len;
    char buf[4096];
    struct iovec iov = { buf, sizeof ( buf ) };
    struct sockaddr_nl sa{};
    struct msghdr msg = { ( void * )&sa, sizeof ( sa ), &iov, 1,
            nullptr, 0, 0 };
    //
    struct nlmsghdr *nh;

    /**
     *blocking
     */
    len = recvmsg ( fd, &msg, 0 );
    if ( len == -1 ) {
        //emit addLog( "netlink recv message error!" );
        //tracer->TraceError( "netlink recv message error!" );
        return;
    }

    //
    for ( nh = ( struct nlmsghdr * ) buf; NLMSG_OK ( nh, ( unsigned int ) len ); nh = NLMSG_NEXT ( nh, len ) ) {
        struct ifinfomsg *ifi_msg;
        /* The end of multipart message. */
        /*
        printf ( "netlink message: len = %u, type = %u, flags = 0x%X, seq = %u, pid = %u\n",
                 nh->nlmsg_len,
                 nh->nlmsg_type,
                 nh->nlmsg_flags,
                 nh->nlmsg_seq,
                 nh->nlmsg_pid );
        */
        if ( nh->nlmsg_type == NLMSG_DONE )
            return;

        if ( nh->nlmsg_type == NLMSG_ERROR ) {
            continue;
        }

        ifi_msg = ( ifinfomsg * ) NLMSG_DATA (nh );

        /*
        printf ( "\tifi_family = %u, ifi_type = %u, ifi_index = %u, ifi_flags = 0x%X, ifi_change = 0x%X\n",
                ifi_msg->ifi_family,
                ifi_msg->ifi_type,
                ifi_msg->ifi_index,
                ifi_msg->ifi_flags,
                ifi_msg->ifi_change );
                */
        //
        /*
        if ( nh->nlmsg_type == RTM_NEWADDR  ) {
            //获得新地址
            OnTrigger();
            return;
        }*/
        print_type ( nh->nlmsg_type );

        print_flags ( ifi_msg->ifi_flags, ifi_msg->ifi_change );
    }
}

//main routine
//run it
int WifiMonitorAction::netlink_notify (  )
{
    //netlink
    struct sockaddr_nl sa;
    int fd;

    memset ( &sa, 0, sizeof ( sa ) );
    //
    sa.nl_family = AF_NETLINK;
    sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;

    //
    fd = socket ( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
    if ( fd == -1 ) {
        add_log(wxT("socket error"));
        return -1;
    }

    //
    if ( bind ( fd, ( struct sockaddr * ) &sa, sizeof ( sa ) ) == -1 ) {
        add_log(wxT("bind error"));
        return -1;
    }
    //
    for ( ;; ) {
        //
        read_msg ( fd );
        //qDebug()<<"for";
    }
    return 0;
}

//
WifiMonitorAction::WifiMonitorAction( ){
    count = 0;
}


bool WifiMonitorAction::Perform() {
    while ( true ) {
        //for: read_msg
        //if fail
        netlink_notify();
        sleep ( 1000 );
    }
    return true;
}

